from functools import reduce

import click

'''
    0.init:
        学习速率：rate,
        权重向量：weights,
        激活函数：activator
    1.Input:
        训练数据：input_vecs,
        数据标签：labels,
    2.calculate:
        divide:划分input_vec,label
        reduce:递归计算w0*x0+...+wn*in,
        update:计算差值更新权重,
        iterate:多次使用数据训练提高精度
    3.predict:
        output:
            weights,rate
'''

class Preceptron:

    __slots__ = ('weights', 'rate', 'activator', 'bias')

    def __str__(self):
        return "\t----", self.weights, '\t----bais:', self.bias

    def __init__(self, param_num, rate, activator):
        self.rate = rate
        self.activator = activator
        self.bias = 0.0
        self.weights = [0.0 for _ in range(param_num)]

    def __str__(self):
        return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias)

    def train(self, iteration, input_vecs, labels):
        for times in range(1, iteration + 1):
            print("现在进行第%d次训练" % times)
            print("\t----", self.weights, '\t----bais:', self.bias)
            self._one_train(input_vecs, labels)

    def _one_train(self, input_vecs, labels):
        for vec, label in zip(input_vecs, labels):
            result = self.__calculate(vec)
            self._update(result, vec, label)

    def __calculate(self, vec):
        return self.predict(vec)

    def _update(self, result, input_vec, label):
        delta = label - result
        self.bias =  self.bias + delta * self.rate
        self.weights = [wi + delta * self.rate * xi for xi, wi in zip(input_vec, self.weights)]

    def predict(self, input_vec):

        result = reduce(lambda x, y: x + y, [xi * wi for xi, wi in zip(input_vec, self.weights)], self.bias)
        result = self.activator(result)
        # print("\t\tpredict result----%d"%result)
        return result

def activator(x):
    return 1 if x > 0 else 0


@click.command()
@click.option('--param_num' , type = click.Choice([2]), help="权重个数")
@click.option('--rate', type = float, help="学习速率")
def call(param_num = 2,rate = 0.1):
    click.echo('param_num is: %f\t  Leaning rate is %f' % (param_num,rate))

if __name__ == '__main__':

    # 期望的输出列表，注意要与输入一一对应
    # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0
    input_vecs = [[1, 1], [0, 0], [1, 0], [0, 1]]
    labels = [1, 0, 0, 0]

    p = Preceptron(2, 0.1, activator)
    p.train(10, input_vecs, labels)

    print(p.__str__())
    print('1 and 1 = %d' % p.predict([1, 1]))
    print('0 and 0 = %d' % p.predict([0, 0]))
    print('1 and 0 = %d' % p.predict([1, 0]))
    print('0 and 1 = %d' % p.predict([0, 1]))